#include <fstream>
#include <iostream>

#include "DwarfMeshLoader.h"
#include "loader/ResourceCollection.h"
#include "util/FileUtil.h"
#include "file/TokenizedFile.h"
#include "math/Vector3.h"

using namespace std;

DwarfMeshLoader::DwarfMeshLoader(ResourceCollection* resources): MeshLoader(resources) {
}

bool DwarfMeshLoader::supports(string path){
	string ext = FileUtil::getFileExtension(path);
	return ext == "dwarfmesh";
}

Mesh* DwarfMeshLoader::load(string file, ShaderProgram* program) {
	TokenizedFile tokenizedFile;
	tokenizedFile.open(file);

	string name;
	vector<Vector3> vertices;
	vector<Vector3> vertNormals;
	vector<Material*> materials;
	vector<Texture*> textures;
	vector<UVLayer*> uvLayers;
	vector<Face*> faces;

	Material* activeMaterial = NULL;
	UVLayer* activeUVLayer = NULL;
	TextureReference* activeTexref = NULL;

	while (tokenizedFile.hasLines()) {
		TokenLine line = tokenizedFile.getLine(' ');

		if (line.isEmpty(0) || line.getString(0) == "#") {
			continue;
		}

		if (line.getString(0) == "Version") {
			// Don't need to handle this just yet
			continue;
		}

		if (line.getString(0) == "Mesh") {
			name = line.getString(1);
			continue;
		}

		if (line.getString(0) == "t") {
			Texture* texture = NULL;
			string textureFile = line.getString(2);

			if (FileUtil::isRelative(textureFile)) {
				texture = resources->loadTexture(FileUtil::getRelativePath(file, textureFile), true);
			} else {
				texture = resources->loadTexture(textureFile, true);
			}

			texture->setName(line.getString(1));
			textures.push_back(texture);
			continue;
		}

		if (line.getString(0) == "UVLayer") {
			activeUVLayer = new UVLayer(line.getString(1));
			uvLayers.push_back(activeUVLayer);
			continue;
		}

		if (line.getString(0) == "uv") {
			Vector3 texcoord;
			texcoord.x = line.getFloat(1);
			texcoord.y = line.getFloat(2);
			texcoord.z = 0.0f;
			activeUVLayer->getTexcoords().push_back(texcoord);
			continue;
		}

		if (line.getString(0) == "m") {
			activeMaterial = resources->newMaterial();
			activeMaterial->setName(line.getString(1));
			materials.push_back(activeMaterial);
			continue;
		}

		if (line.getString(0) == "diffuse") {
			RGBA diffuse(line.getFloat(1), line.getFloat(2), line.getFloat(3), 1.0);
			activeMaterial->setDiffuse(diffuse);
			continue;
		}

		if (line.getString(0) == "specular") {
			RGBA specular(line.getFloat(1), line.getFloat(2), line.getFloat(3), 1.0);
			activeMaterial->setSpecular(specular);
			continue;
		}

		if (line.getString(0) == "tr") {
			activeTexref = new TextureReference();
			string textureName = line.getString(1);
			string uvLayerName = line.getString(2);

			for (unsigned int i = 0; i < textures.size(); i++) {
				if (textures[i]->getName() == textureName) {
					activeTexref->setTexture(textures[i]);
					break;
				}
			}

			for (unsigned int i = 0; i < uvLayers.size(); i++) {
				if (uvLayers[i]->getName() == uvLayerName) {
					activeTexref->setUvLayer(uvLayers[i]);
					break;
				}
			}

			activeMaterial->getTexrefs().push_back(activeTexref);
			continue;
		}

		if (line.getString(0) == "blend") {
			string mode = line.getString(1);
			activeTexref->setBlendFactor(line.getFloat(2));

			if (mode == "MIX") {
				activeTexref->setBlendMode(MIX);
			}
			else if (mode == "MULTIPLY") {
				activeTexref->setBlendMode(MULTIPLY);
			}
			continue;
		}

		if (line.getString(0) == "v") {
			Vector3 vert(line.getFloat(1), line.getFloat(2), line.getFloat(3));
			Vector3 normal(line.getFloat(4), line.getFloat(5), line.getFloat(6));
			vertices.push_back(vert);
			vertNormals.push_back(normal);
			continue;
		}

		if (line.getString(0) == "f") {
			Face* face = new Face;
			face->material = materials[line.getInt(1)];
			face->vertices.push_back(line.getInt(2));
			face->vertices.push_back(line.getInt(3));
			face->vertices.push_back(line.getInt(4));
			face->vertexNormals.push_back(line.getInt(2));
			face->vertexNormals.push_back(line.getInt(3));
			face->vertexNormals.push_back(line.getInt(4));
			faces.push_back(face);
			continue;
		}
	}

	Mesh* mesh = new Mesh(vertices, vertNormals, textures, uvLayers, materials, faces, program);
	mesh->setName(name);
	mesh->compile();
	resources->put(mesh);
	return mesh;
}



DwarfMeshLoader::~DwarfMeshLoader() {
}
